package de.lgohlke.concurrent;

import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * wrapper class for fixedSizeThreadPool
 * 
 * @author lars
 * @version $Id: $
 */
public class ThreadPool
{
  private final static Logger    log             = LoggerFactory.getLogger(ThreadPool.class);
  private static final long      SLEEPTIME       = 50;

  private final ExecutorService  executorService;
  private final int              threadPoolSize;
  private final Queue<Exception> exceptions      = new ConcurrentLinkedQueue<Exception>();
  private boolean                printExceptions = true;

  /**
   * <p>
   * Constructor for ThreadPool.
   * </p>
   * 
   * @param maxcores
   *          a int.
   */
  public ThreadPool(final int maxcores)
  {
    threadPoolSize = maxcores;
    this.executorService = Executors.newFixedThreadPool(maxcores);
  }

  /**
   * creates an instance with 2*availableProcessores-Thread
   * 
   * @return a {@link de.lgohlke.concurrent.ThreadPool} object.
   */
  public static ThreadPool getInstance()
  {
    return new ThreadPool(Runtime.getRuntime().availableProcessors() * 2);
  }

  /**
   * creates an instance with availableProcessores-Thread
   * 
   * @return a {@link de.lgohlke.concurrent.ThreadPool} object.
   */
  public static ThreadPool getNaturalInstance()
  {
    return new ThreadPool(Runtime.getRuntime().availableProcessors());
  }

  /**
   * <p>
   * Getter for the field <code>threadPoolSize</code>.
   * </p>
   * 
   * @return a int.
   */
  public int getThreadPoolSize()
  {
    return threadPoolSize;
  }

  /**
   * add a new task
   * 
   * @param task
   *          a {@link java.lang.Runnable} object.
   * @return a {@link java.util.concurrent.Future} object.
   */
  public Future<?> submit(final Runnable task)
  {
    return executorService.submit(new Runnable()
    {
      @Override
      public void run()
      {
        try
        {
          task.run();
        }
        catch (Exception e)
        {
          if (printExceptions)
          {
            log.error(e.getMessage(), e);
          }
          exceptions.add(e);
        }
      }
    });
  }

  /**
   * {@inheritDoc}
   * 
   * @see {@link ThreadPoolExecutor#getActiveCount()}
   * @return a int.
   */
  public int getRunningJobs()
  {
    return ((ThreadPoolExecutor) executorService).getActiveCount();
  }

  /**
   * jobs that are not already started
   * 
   * @return a {@link java.util.Queue} object.
   */
  public Queue<Runnable> getEnquedJobs()
  {
    return ((ThreadPoolExecutor) executorService).getQueue();
  }

  /**
   * <p>
   * getCompleted.
   * </p>
   * 
   * @return a long.
   */
  public long getCompleted()
  {
    return ((ThreadPoolExecutor) executorService).getCompletedTaskCount();
  }

  /**
   * waiting for old running jobs without accepting new ones
   */
  public void waitForShutdown()
  {
    executorService.shutdown();
    while (!executorService.isTerminated())
    {
      try
      {
        Thread.sleep(SLEEPTIME);
      }
      catch (InterruptedException e)
      {
        // ok
      }
    }
  }

  /**
   * <p>
   * Getter for the field <code>executorService</code>.
   * </p>
   * 
   * @return a {@link java.util.concurrent.ExecutorService} object.
   */
  public ExecutorService getExecutorService()
  {
    return executorService;
  }

  /**
   * <p>
   * Getter for the field <code>exceptions</code>.
   * </p>
   * 
   * @return a {@link java.util.Queue} object.
   */
  public Queue<Exception> getExceptions()
  {
    return exceptions;
  }

  /**
   * <p>
   * Setter for the field <code>printExceptions</code>.
   * </p>
   * 
   * @param printExceptions
   *          a boolean.
   */
  public void setPrintExceptions(final boolean printExceptions)
  {
    this.printExceptions = printExceptions;
  }

  /**
   * <p>
   * isPrintExceptions.
   * </p>
   * 
   * @return a boolean.
   */
  public boolean isPrintExceptions()
  {
    return printExceptions;
  }

  /**
   * {@inheritDoc}
   * 
   * @see {@link ExecutorService#shutdownNow()}
   * @return a {@link java.util.List} object.
   */
  public List<Runnable> shutdownNow()
  {
    return executorService.shutdownNow();
  }

  /**
   * wait until no more jobs are enqueued
   */
  public void waitForPoolEmpty()
  {
    while (!executorService.isTerminated() && !getEnquedJobs().isEmpty())
    {
      try
      {
        if (!getEnquedJobs().isEmpty())
        {
          Thread.sleep(SLEEPTIME);
        }
      }
      catch (InterruptedException e)
      {
        // ok
      }
    }

  }
}
